#ifndef __M_THRPOOL_H__
#define __M_THRPOOL_H__

#include<iostream>
#include<functional>
#include<memory>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<future>
#include<vector>

/*
    promise 需要在函数中添加一个参数 不可能给用户传来的函数加一个参数 不使用
    async 是系统创建自带的工作线程 不使用
*/
/*
    管理成员： 
    任务池：用vector维护的函数任务池子
    互斥锁&条件变量： 用于实现同步互斥
    一定数量的工作线程： 用于不断从任务池取出执行任务
    结束运行标指： 以便控制线程池结束

    管理操作：
    入队任务： 入队一个函数和参数
    停止运行： 终止线程池
*/

class threadPool
{
public:
    using ptr = std::shared_ptr<threadPool>;
    //std::function<void(void)> 是一种泛型函数对象，它能够绑定任何可调用的目标
    using Functor = std::function<void(void)>; 
    threadPool(int thr_count = 1)
        :_stop(false)
    { 
        for(int i = 0; i < thr_count; i++)
        {
            //emplace_back 插入同时可以构造
            _threads.emplace_back(&threadPool::entry, this);
        }
    }
    ~threadPool()
    {
        stop();
    }
    //函数push 传入的首先有一个函数 用户要执行的函数 接下来是不定参 表示用户要传入的参数
    //push函数内部 会将传入的函数封装成一个异步任务（packaged_task） 抛入线程池 由工作线程取出
    //...args表示参数包整体 args...表示一个个
    //future<?> push(F &&func, Args&& ...arg)  
    template<typename F, typename ...Args>
    auto push(F &&funct, Args&& ...args) -> std::future<decltype(funct(args...))>
    {
        //1.将传入的函数封装成一个packaged——task包
        using return_type = decltype(funct(args...));
        auto func = std::bind(std::forward<F>(funct), std::forward<Args>(args)...); //std::packaged_task<return_type()> task(func) 临时变量不用 用堆上指针
        auto task = std::make_shared<std::packaged_task<return_type()> >(func);
        std::future<return_type> fu = task->get_future();               //绑定
        //2.构造一个lambda匿名函数(捕获任务对象)， 函数内执行任务对象
        {
            std::unique_lock<std::mutex> lock(_mutex);  //花括号是限定作用域
            //3.将构造出来的匿名函数对象， 抛入任务池中
            _taskpool.push_back( [task](){ return (*task)(); });
            _cv.notify_one();
        }
        return fu;
    }
    void stop()
    {
        //std::cout << std::this_thread::get_id() << " 尝试执行stop" << std::endl;
        //防止析构和主动停止 冲突
        if(_stop == true)   return;
        _stop = true;
        _cv.notify_all();           //唤醒所有线程
        for(auto& thread : _threads)
        {
            thread.join();          //主线程等待所有线程
        }
    }
private:
    //线程入口函数 不断从任务池中取出任务进行执行
    void entry()
    {
        while(!_stop)
        {
            // 防止一个一个取一直加锁解锁耗时间 直接创建临时变量 一个线程将所有任务取出
            // 由于tmp_taskpool是局部变量 不用担心线程安全问题
            std::vector<Functor> tmp_taskpool;
            // 限定锁作用域
            {
                // 1.加锁
                std::unique_lock<std::mutex> lock(_mutex);
                // 2.等待任务池不为空 或者_stop 被置位返回
                /*
                    wait（unique_lock <mutex>＆lck，Predicate pred）
                    当前线程仅在pred=false时阻塞；如果pred=true时，不阻塞。
                 */
                //_cv.wait(lock, _stop || !_taskpool.empty());  //ThreadPool私有成员 创建的thread获取不到
                _cv.wait(lock, [this]() { return _stop || !_taskpool.empty(); });
                // 3.取出任务执行
                tmp_taskpool.swap(_taskpool);
            }
            for (auto &task : tmp_taskpool)
            {
                task();
            }
        }
    } 
private: 
    std::atomic<bool> _stop;
    std::vector<Functor> _taskpool;
    //互斥锁和条件变量要放在线程前
    std::mutex _mutex;
    std::condition_variable _cv;
    std::vector<std::thread> _threads;
};

int Add(int num1, int num2)
{
    return num1 + num2;
}


#endif